﻿using MapEditorXna.Background;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using MapEditorXna.Ui;
using ZeldoEngine.Game.Maps;
using ZeldoEngineObjects.Maps;
using ZeldoEngine;
using ZeldoEngineServices.Maps;
using ZeldoEngineObjects;

namespace MapEditorXna
{
    public class Controller
    {
        private Map _currentMap;
        private EditorBackground _background;
        private Selection _selection;

        public GraphicsDevice GraphicDevice { get; set; }
        public ContentManager Content { get; set; }

        public int OffsetX { get; set; }
        public int OffsetY { get; set; }

        public void Initialize(GraphicsDevice graphiDevice)
        {
            this._background = new EditorBackground();
            this._currentMap = new Map();
            this._selection = new Selection();
            this.GraphicDevice = graphiDevice;
        }

        public void Resize(int width, int height)
        {
            this._background.SetSize(width, height);
        }

        public void LoadContent(ContentManager content)
        {
            this.Content = content;

            this._background.LoadContent(content);
            this._selection.Load(content, this.GraphicDevice);
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            this._background.Draw(spriteBatch);

            if (this._currentMap != null && this._currentMap.Model != null)
            {
                this._background.DrawMapBackground(spriteBatch, this._currentMap.Width * EngineConsts.TILE_SIZE, this._currentMap.Height * EngineConsts.TILE_SIZE);
                this._currentMap.Dessiner(spriteBatch, MapModel.Layer.Level0, this.OffsetX, this.OffsetY);
                this._currentMap.Dessiner(spriteBatch, MapModel.Layer.Level1, this.OffsetX, this.OffsetY);
                this._currentMap.Dessiner(spriteBatch, MapModel.Layer.Level2, this.OffsetX, this.OffsetY);
                this._selection.Draw(spriteBatch);
            }
        }

        public void LoadMap(MapModel m)
        {
            this.OffsetX = 0;
            this.OffsetY = 0;

            this._currentMap.ChargerCarte(this.Content, m);
        }

        public void SetCursorPosition(int x, int y, int width, int height)
        {
            if (this._currentMap != null)
            {
                if (x >= 0 && x < (this._currentMap.Width * EngineConsts.TILE_SIZE) && y >= 0 && y < (this._currentMap.Height * EngineConsts.TILE_SIZE))
                {
                    this._selection.X = x;
                    this._selection.Y = y;
                    this._selection.Width = width;
                    this._selection.Height = height;
                    this._selection.Show = true;
                }
                else
                {
                    this._selection.Show = false;
                }
            }
        }

        public void Click(int x, int y, int width, int height, int startTile, int layerNumber)
        {
            if (this._currentMap != null)
            {
                if (x >= 0 && x < (this._currentMap.Width * EngineConsts.TILE_SIZE) && y >= 0 && y < (this._currentMap.Height * EngineConsts.TILE_SIZE))
                {
                    var l = MapModel.Layer.Level0;

                    switch (layerNumber)
                    {
                        case 0: l = MapModel.Layer.Level0; break;
                        case 1: l = MapModel.Layer.Level1; break;
                        case 2: l = MapModel.Layer.Level2; break;
                    }

                    var tileX = x / EngineConsts.TILE_SIZE - this.OffsetX;
                    var tileY = y / EngineConsts.TILE_SIZE - this.OffsetY;

                    var tileSizeX = width / EngineConsts.TILE_SIZE;
                    var tileSizeY = height / EngineConsts.TILE_SIZE;

                    for (var j = 0; j < tileSizeY; j++)
                    {
                        for (var i = 0; i < tileSizeX; i++)
                        {
                            var tuileIndex = (uint)(startTile + i + (j * EngineConsts.CHIPSET_TILES_PER_LINE));

                            var xMap = tileX + i;
                            var yMap = tileY + j;

                            // Si nous sommes en autotile, il faut calculer les bords
                            if (tuileIndex > 0 && tuileIndex < EngineConsts.CHIPSET_TILES_PER_LINE)
                            {
                                tuileIndex = this.AppliquerAutotiling(tuileIndex, xMap, yMap, l);

                                MapService.SetTile(this._currentMap.Model, l, xMap, yMap, tuileIndex);

                                var autotileOffsetX = xMap - 1;
                                var autotileOffsetY = yMap - 1;
                                var t = MapService.GetCompletTileInfos(this._currentMap.Model, l, autotileOffsetX, autotileOffsetY);
                                if (t != null)
                                {
                                    var nt = this.AppliquerAutotiling(t.Value, autotileOffsetX, autotileOffsetY, l);
                                    MapService.SetTile(this._currentMap.Model, l, autotileOffsetX, autotileOffsetY, nt);
                                }

                                autotileOffsetX = xMap;
                                autotileOffsetY = yMap - 1;
                                t = MapService.GetCompletTileInfos(this._currentMap.Model, l, autotileOffsetX, autotileOffsetY);
                                if (t != null)
                                {
                                    var nt = this.AppliquerAutotiling(t.Value, autotileOffsetX, autotileOffsetY, l);
                                    MapService.SetTile(this._currentMap.Model, l, autotileOffsetX, autotileOffsetY, nt);
                                }

                                autotileOffsetX = xMap + 1;
                                autotileOffsetY = yMap - 1;
                                t = MapService.GetCompletTileInfos(this._currentMap.Model, l, autotileOffsetX, autotileOffsetY);
                                if (t != null)
                                {
                                    var nt = this.AppliquerAutotiling(t.Value, autotileOffsetX, autotileOffsetY, l);
                                    MapService.SetTile(this._currentMap.Model, l, autotileOffsetX, autotileOffsetY, nt);
                                }

                                autotileOffsetX = xMap - 1;
                                autotileOffsetY = yMap;
                                t = MapService.GetCompletTileInfos(this._currentMap.Model, l, autotileOffsetX, autotileOffsetY);
                                if (t != null)
                                {
                                    var nt = this.AppliquerAutotiling(t.Value, autotileOffsetX, autotileOffsetY, l);
                                    MapService.SetTile(this._currentMap.Model, l, autotileOffsetX, autotileOffsetY, nt);
                                }

                                autotileOffsetX = xMap + 1;
                                autotileOffsetY = yMap;
                                t = MapService.GetCompletTileInfos(this._currentMap.Model, l, autotileOffsetX, autotileOffsetY);
                                if (t != null)
                                {
                                    var nt = this.AppliquerAutotiling(t.Value, autotileOffsetX, autotileOffsetY, l);
                                    MapService.SetTile(this._currentMap.Model, l, autotileOffsetX, autotileOffsetY, nt);
                                }

                                autotileOffsetX = xMap - 1;
                                autotileOffsetY = yMap + 1;
                                t = MapService.GetCompletTileInfos(this._currentMap.Model, l, autotileOffsetX, autotileOffsetY);
                                if (t != null)
                                {
                                    var nt = this.AppliquerAutotiling(t.Value, autotileOffsetX, autotileOffsetY, l);
                                    MapService.SetTile(this._currentMap.Model, l, autotileOffsetX, autotileOffsetY, nt);
                                }

                                autotileOffsetX = xMap;
                                autotileOffsetY = yMap + 1;
                                t = MapService.GetCompletTileInfos(this._currentMap.Model, l, autotileOffsetX, autotileOffsetY);
                                if (t != null)
                                {
                                    var nt = this.AppliquerAutotiling(t.Value, autotileOffsetX, autotileOffsetY, l);
                                    MapService.SetTile(this._currentMap.Model, l, autotileOffsetX, autotileOffsetY, nt);
                                }

                                autotileOffsetX = xMap + 1;
                                autotileOffsetY = yMap + 1;
                                t = MapService.GetCompletTileInfos(this._currentMap.Model, l, autotileOffsetX, autotileOffsetY);
                                if (t != null)
                                {
                                    var nt = this.AppliquerAutotiling(t.Value, autotileOffsetX, autotileOffsetY, l);
                                    MapService.SetTile(this._currentMap.Model, l, autotileOffsetX, autotileOffsetY, nt);
                                }
                            }
                            else
                            {
                                MapService.SetTile(this._currentMap.Model, l, xMap, yMap, tuileIndex);
                            }
                        }
                    }
                }
            }
        }

        private uint AppliquerAutotiling(uint tile, int xMap, int yMap, MapModel.Layer l)
        {
            var tuileHautGauche = MapService.GetCompletTileInfos(this._currentMap.Model, l, xMap - 1, yMap - 1);
            var tuileHautMilieu = MapService.GetCompletTileInfos(this._currentMap.Model, l, xMap, yMap - 1);
            var tuileHautDroit = MapService.GetCompletTileInfos(this._currentMap.Model, l, xMap + 1, yMap - 1);

            var tuileGauche = MapService.GetCompletTileInfos(this._currentMap.Model, l, xMap - 1, yMap);
            var tuileDroit = MapService.GetCompletTileInfos(this._currentMap.Model, l, xMap + 1, yMap);

            var tuileBasGauche = MapService.GetCompletTileInfos(this._currentMap.Model, l, xMap - 1, yMap + 1);
            var tuileBasMilieu = MapService.GetCompletTileInfos(this._currentMap.Model, l, xMap, yMap + 1);
            var tuileBasDroit = MapService.GetCompletTileInfos(this._currentMap.Model, l, xMap + 1, yMap + 1);

            var tIndex = (tile & 0xFFF);

            if (!tuileHautGauche.HasValue) { tuileHautGauche = new uint?(tile & (0xFFF)); }
            if (!tuileHautMilieu.HasValue) { tuileHautMilieu = new uint?(tile & (0xFFF)); }
            if (!tuileHautDroit.HasValue) { tuileHautDroit = new uint?(tile & (0xFFF)); }
            if (!tuileGauche.HasValue) { tuileGauche = new uint?(tile & (0xFFF)); }
            if (!tuileDroit.HasValue) { tuileDroit = new uint?(tile & (0xFFF)); }
            if (!tuileBasGauche.HasValue) { tuileBasGauche = new uint?(tile & (0xFFF)); }
            if (!tuileBasMilieu.HasValue) { tuileBasMilieu = new uint?(tile & (0xFFF)); }
            if (!tuileBasDroit.HasValue) { tuileBasDroit = new uint?(tile & (0xFFF)); }

            // Coin haut gauche du tile
            if (tIndex == (tuileGauche.Value & 0xFFF) || tIndex == (tuileHautMilieu.Value & 0xFFF))
            {
                tile = tile & 0xFF8FFFF;
                if (tIndex == (tuileGauche.Value & 0xFFF) && tIndex == (tuileHautMilieu.Value & 0xFFF) && tIndex == (tuileHautGauche.Value & 0xFFF))
                {
                    tile = tile | (0x4 << 16);
                }
                else if (tIndex == (tuileGauche.Value & 0xFFF) && tIndex == (tuileHautMilieu.Value & 0xFFF))
                {
                    tile = tile | (0x3 << 16);
                }
                else if (tIndex == (tuileHautMilieu.Value & 0xFFF))
                {
                    tile = tile | (0x2 << 16);
                }
                else if (tIndex == (tuileGauche.Value & 0xFFF))
                {
                    tile = tile | (0x1 << 16);
                }
            }

            // Coin haut droit du tile
            if (tIndex == (tuileDroit.Value & 0xFFF) || tIndex == (tuileHautMilieu.Value & 0xFFF))
            {
                tile = tile & 0xFC7FFFF;
                if (tIndex == (tuileDroit.Value & 0xFFF) && tIndex == (tuileHautMilieu.Value & 0xFFF) && tIndex == (tuileHautDroit.Value & 0xFFF))
                {
                    tile = tile | (0x4 << 19);
                }
                else if (tIndex == (tuileDroit.Value & 0xFFF) && tIndex == (tuileHautMilieu.Value & 0xFFF))
                {
                    tile = tile | (0x3 << 19);
                }
                else if (tIndex == (tuileHautMilieu.Value & 0xFFF))
                {
                    tile = tile | (0x2 << 19);
                }
                else if (tIndex == (tuileDroit.Value & 0xFFF))
                {
                    tile = tile | (0x1 << 19);
                }
            }

            // Coin bas gauche du tile
            if (tIndex == (tuileGauche.Value & 0xFFF) || tIndex == (tuileBasMilieu.Value & 0xFFF))
            {
                tile = tile & 0xE3FFFFF;
                if (tIndex == (tuileGauche.Value & 0xFFF) && tIndex == (tuileBasMilieu.Value & 0xFFF) && tIndex == (tuileBasGauche.Value & 0xFFF))
                {
                    tile = tile | (0x4 << 22);
                }
                else if (tIndex == (tuileGauche.Value & 0xFFF) && tIndex == (tuileBasMilieu.Value & 0xFFF))
                {
                    tile = tile | (0x3 << 22);
                }
                else if (tIndex == (tuileBasMilieu.Value & 0xFFF))
                {
                    tile = tile | (0x2 << 22);
                }
                else if (tIndex == (tuileGauche.Value & 0xFFF))
                {
                    tile = tile | (0x1 << 22);
                }
            }


            // Coin bas droit du tile
            if (tIndex == (tuileDroit.Value & 0xFFF) || tIndex == (tuileBasMilieu.Value & 0xFFF))
            {
                tile = tile & 0x1FFFFFF;
                if (tIndex == (tuileDroit.Value & 0xFFF) && tIndex == (tuileBasMilieu.Value & 0xFFF) && tIndex == (tuileBasDroit.Value & 0xFFF))
                {
                    tile = tile | (0x4 << 25);
                }
                else if (tIndex == (tuileDroit.Value & 0xFFF) && tIndex == (tuileBasMilieu.Value & 0xFFF))
                {
                    tile = tile | (0x3 << 25);
                }
                else if (tIndex == (tuileBasMilieu.Value & 0xFFF))
                {
                    tile = tile | (0x2 << 25);
                }
                else if (tIndex == (tuileDroit.Value & 0xFFF))
                {
                    tile = tile | (0x1 << 25);
                }
            }

            return tile;
        }
    }
}
